package com.example.tobebigfe.web

import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import okhttp3.*
import okio.buffer
import okio.source
import org.json.JSONObject
import java.io.IOException
import java.io.InputStream

typealias ManifestUpdatedCallback = (error: Throwable?) -> Unit

class VersionManager(val context: Context, val httpClient: OkHttpClient, val preferences: SharedPreferences) {


    private val manifestKey = "web.manifest"

    // 项目名 => 版本
    private val builtinMap = mutableMapOf<String, String>()
    private val versionMap = mutableMapOf<String, String>()
    private var manifestUpdating: Boolean = false
    private var updateCallbacks = mutableListOf<ManifestUpdatedCallback>()

    init {
        // 1. 从assets中获取manifest.json的InputStream
        val input = context.assets.open("manifest.json")
        // 2. 读取完整内容到builtinMap
        val builtinManifestStr = readAll(input)
        parseManifest(builtinManifestStr, builtinMap)
        // 3. 拷贝一份到versionMap
        builtinMap.forEach { versionMap[it.key] = it.value }

        // 4. 解析本地更新到的版本
        val manifestStr = preferences.getString(manifestKey, null)
        if (!manifestStr.isNullOrBlank()) {
            parseManifest(manifestStr, versionMap)
        }
    }

    fun getVersion(id: String): String? {
        return versionMap[id]
    }

    fun getBuiltinVersion(id: String): String? {
        return builtinMap[id]
    }

    fun updateManifest(callback: ManifestUpdatedCallback?): Call? {
        if (manifestUpdating) {
            if (callback != null) {
                updateCallbacks.add(callback)
            }
            return null
        }

        Log.e("WebManager", "update manifest")

        manifestUpdating = true
        if (callback != null) {
            updateCallbacks.add(callback)
        }

        val url = "${WebConst.WEB_BASE_URL}/manifest.json?t=${System.currentTimeMillis()}"
        val request = Request.Builder()
            .url(url)
            .build()

        val call = httpClient.newCall(request)
        call.enqueue(object: Callback {

            fun callbackAll(error: Throwable?) {
                Log.e("WebManager", "update manifest complete. ${error?.localizedMessage ?: ""}")
                updateCallbacks.forEach {
                    it.invoke(error)
                }
                updateCallbacks.clear()
                manifestUpdating = false
            }

            override fun onFailure(call: Call, e: IOException) {
                callbackAll(e)
            }

            override fun onResponse(call: Call, response: Response) {
                if (!response.isSuccessful) {
                    callbackAll(IOException("Request fail: ${response.code}."))
                    return
                }

                val bodyStr = response.body?.string()
                if (bodyStr == null) {
                    callbackAll(IOException("Response body error."))
                    return
                }

                try {
                    parseManifest(bodyStr, versionMap)
                    preferences.edit().putString(manifestKey, bodyStr).apply()
                } catch (error: Throwable) {
                    callbackAll(error)
                    return
                }
                callbackAll(null)
            }
        })
        return call
    }

    private fun parseManifest(content: String, output: MutableMap<String, String>) {
        val manifest = JSONObject(content)
        val projects = manifest.getJSONArray("projects")
        for (i in 0 until projects.length()) {
            val prj = projects.getJSONObject(i)
            output[prj.getString("name")] = prj.getString("version")
        }
    }

    // 工具函数，读取一个InputStream的所有内容，这里使用到了okio库，非常方便
    @Throws(IOException::class)
    private fun readAll(input: InputStream): String {
        return input.source().use { fileSource ->
            fileSource.buffer().use { buffer ->
                buffer.readUtf8()
            }
        }
    }

}